﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using pwestcott.Util;

namespace pwestcott.Utils.PerformanceTests.Essence
{
    class GetHashCodeTests : ITest
    {
        public interface IMyData
        {
            int MyInt { get; set; }
            string MyString { get; set; }
            DateTime MyDateTime { get; set; }
        }

        [EssenceClass(UseIn = EssenceFunctions.All)]
        class TestEssenceStatic : IMyData
        {
            static readonly Func<TestEssenceStatic, int> getHashCode = Essence<TestEssenceStatic>.GetHashCodeStatic;

            [Essence(Order = 0)] public int MyInt { get; set; }
            [Essence(Order = 1)] public string MyString { get; set; }
            [Essence(Order = 2)] public DateTime MyDateTime { get; set; }

            public override int GetHashCode()
            {
                return getHashCode(this);
            }
        }

        class TestEssenceMember : IMyData
        {
            private readonly Func<int> getHashCode;

            [Essence(Order = 0)] public int MyInt { get; set; }
            [Essence(Order = 1)] public string MyString { get; set; }
            [Essence(Order = 2)] public DateTime MyDateTime { get; set; }

            public TestEssenceMember()
            {
                getHashCode = Essence<TestEssenceMember>.GetHashCodeInstance(this);
            }

            public override int GetHashCode()
            {
                return getHashCode();
            }
        }

        class TestManual : IMyData
        {
            public int MyInt { get; set; }
            public string MyString { get; set; }
            public DateTime MyDateTime { get; set; }

            public override int GetHashCode()
            {
                var x = MyInt.GetHashCode();
                x *= Essence<TestEssenceStatic>.HashCodeMultiplier;
                x ^= (MyString == null) ? 0 : MyString.GetHashCode();
                x *= Essence<TestEssenceStatic>.HashCodeMultiplier;
                x ^= MyDateTime.GetHashCode();
                return x;
            }
        }

        public int TestObj(string name, int iterations, IMyData x)
        {
            int total = 0;

            DateTime start = DateTime.Now;
            for (int i = 0; i < iterations; ++i)
            {
                unchecked
                {
                    x.MyInt++;
                    total += x.GetHashCode();
                }
            }
            DateTime end = DateTime.Now;

            Console.WriteLine("{0} {1}", name, (end-start).TotalMilliseconds);

            return total;
        }

        public void Run()
        {
            const int iterations = 1000000;

            var staticTest = new TestEssenceStatic();
            var memberTest = new TestEssenceMember();
            var manualTest = new TestManual();

            staticTest.MyDateTime = memberTest.MyDateTime = manualTest.MyDateTime = DateTime.Now;
            staticTest.MyInt = memberTest.MyInt = manualTest.MyInt = 42;
            staticTest.MyString = memberTest.MyString = manualTest.MyString = "The Quick Brown Fox Jumps Over The Lazy Dog";

            var sanity1 = TestObj("static", iterations, staticTest);
            var sanity3 = TestObj("member", iterations, memberTest);
            var sanity2 = TestObj("manual", iterations, manualTest);

            if (sanity1 == sanity2 && sanity1 == sanity3)
                Console.WriteLine("Sanity check passed ({0})", sanity1);
            else
                Console.WriteLine("Sanity check failed ({0}, {1}, {2})", sanity1, sanity2, sanity3);
        }
    }
}
